#include "adc.hpp"
#include "apm32f0xx_gpio.h"
#include "apm32f0xx_dma.h"
#include "apm32f0xx_adc.h"
#include "apm32f0xx_rcm.h"
#include "apm32f0xx_misc.h"
#include "stdio.h"
#include "system.hpp"

volatile uint16_t __adc_data[2] = {0};

bool ADCUtil::init(void){
    GPIO_Config_T gpioConfig;
    ADC_Config_T  adcConfig;    
    System_setPeripheralClockEnable(CLK_GPIOC,TRUE);
    System_setPeripheralClockEnable(CLK_ADC,TRUE);
    System_setPeripheralClockEnable(CLK_SYSCFG,TRUE);

    /* CGF */  
    gpioConfig.pin = GPIO_PIN_0;
    gpioConfig.mode = GPIO_MODE_AN;
    gpioConfig.pupd = GPIO_PUPD_PU;
    GPIO_Config(GPIOC, &gpioConfig);
    /* Case */
    gpioConfig.pin = GPIO_PIN_1;
    GPIO_Config(GPIOC, &gpioConfig);
    /* ADC Configuration */
    ADC_Reset();
    ADC_ConfigStructInit(&adcConfig);
    adcConfig.resolution = ADC_RESOLUTION_12B;
    adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT;
    adcConfig.scanDir = ADC_SCAN_DIR_UPWARD;
    adcConfig.convMode = ADC_CONVERSION_CONTINUOUS;
    adcConfig.extTrigConv = ADC_EXT_TRIG_CONV_TRG0;
    adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_NONE;

    ADC_Config(&adcConfig);

    ADC_ConfigChannel(ADC_CHANNEL_10, ADC_SAMPLE_TIME_239_5);
    ADC_ConfigChannel(ADC_CHANNEL_11, ADC_SAMPLE_TIME_239_5);
    ADC_ReadCalibrationFactor();

    ADC_EnableDMA();
    ADC_DMARequestMode(ADC_DMA_MODE_CIRCULAR);
    ADC_Enable();

    /* DMA Init*/
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);    
    DMA_Config_T dmaConfig;
    dmaConfig.bufferSize = 2;
    dmaConfig.memoryDataSize = DMA_MEMORY_DATASIZE_HALFWORD;
    dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATASIZE_HALFWORD;
    dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE;
    dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
    dmaConfig.circular = DMA_CIRCULAR_ENABLE;
    dmaConfig.memoryTomemory = DMA_M2M_DISABLE;
    dmaConfig.priority = DMA_PRIORITY_LEVEL_HIGHT;
    dmaConfig.direction = DMA_DIR_PERIPHERAL;
    dmaConfig.memoryAddress = (uint32_t)__adc_data;
    dmaConfig.peripheralAddress = (uint32_t)&ADC->DATA;

    DMA_Config(DMA1_CHANNEL_1, &dmaConfig);
    DMA_ClearIntFlag(DMA1_INT_FLAG_TF1);
    DMA_EnableInterrupt(DMA1_CHANNEL_1, DMA_INT_TFIE);
    NVIC_EnableIRQRequest(DMA1_CH1_IRQn, 2);
    DMA_Enable(DMA1_CHANNEL_1);
    while (!ADC_ReadStatusFlag(ADC_FLAG_ADRDY));
    ADC_StartConversion();
    return true;
}

double ADCUtil::getChannel(Channel channel){
    int channel_num = 0;
    switch ( channel ){
        case Channel::CGF:channel_num = 0; break;
        case Channel::Case: channel_num = 1; break;

        default: break;
    }
    return __adc_data[channel_num] * 3300.0 / 4096.0 / 1000;
}

static ADCUtil *_adc = nullptr;

ADCUtil *ADCUtil::getInstance(){
    if( _adc == nullptr){
        _adc = new ADCUtil();
        _adc->init();
    }
    return _adc;
}

extern "C"
void DMA1_CH1_IRQHandler(void)
{
    if (DMA_ReadStatusFlag(DMA1_FLAG_TF1))
    {
        /* do something*/
        DMA_ClearStatusFlag(DMA1_FLAG_TF1);
    }
}